
#include <BRender>
#include <BLight>
#include <BPainter>
#include <BEvent>
#include <BMouseEvent>

#include <BViewer>
#include "member_BWidget.h"
#include "member_BRender.h"
#include "member_BViewer.h"
#include "member_BHandler.h"
#include "member_BCamera.h"
#include "member_BNode.h"
#include "member_BScene.h"
#include "member_BPicker.h"

using namespace BWE;

#define member					(*(member_BViewer*)_ptr)
#define member_allocate()		_ptr = new member_BViewer(this)
#define member_release()		delete (member_BViewer*)_ptr

BViewer::BViewer(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BViewer::BViewer(const BString& title, BWidget* parent) : BWidget(title, parent)
{
	member_allocate();
}
BViewer::~BViewer()
{
	member_release();
}

void BViewer::setScene(BScene* scene)
{
	if (member.scene != scene)
	{
		if (member.scene)
			member.disconnect(member.scene);
		member.scene = scene;
		if (member.scene)
			member.connect(member.scene, Signal_Fresh, &member_BViewer::slotSceneFresh);
		this->fresh();
	}
}
BScene* BViewer::scene() const
{
	return member.scene;
}

void BViewer::setCamera(BCamera* camera)
{
	if (member.camera != camera)
	{
		if (member.camera)
		{
			member.disconnect(member.camera);
		}
		member.camera = camera;
		if (member.camera)
		{
			if (member.camera->pixelSizePolicy() == Policy_Dynamic)
			{
				const BRect& crect = this->centerRect();
				member.camera->setPixelSize(crect.size());
				BReal aspect = (BReal)crect.width() / crect.height();
				member.camera->setAspect(aspect);
			}
			member.connect(member.camera, Signal_Changed, &member_BViewer::slotCameraChanged);
			member.connect(member.camera, Signal_Fresh, &member_BViewer::slotCameraFreshed);
		}
		this->fresh();
	}
}
BCamera* BViewer::camera()
{
	return member.camera.ptr();
}
const BCamera* BViewer::camera() const
{
	return member.camera.ptr();
}

void BViewer::setHandler(BHandler* handler)
{
	if (member.handler != handler)
	{
		BHandlerHolder temp = member.handler;
		member.handler = handler;
		if (temp)
			handler_member(temp)->setViewer(0);
		if (handler)
			handler_member(handler)->setViewer(this);
		this->emit(Signal_HandlerChanged, handler);
		this->fresh();
	}
}
BHandler* BViewer::handler()
{
	return member.handler;
}
const BHandler* BViewer::handler() const
{
	return member.handler;
}

void BViewer::setPicker(BPicker* picker)
{
	if (member.picker != picker)
	{
		member.picker = picker;
	}
}
BPicker* BViewer::picker()
{
	if (member.picker.empty())
		member.picker = new BPicker();
	return member.picker;
}
const BPicker* BViewer::picker() const
{
	return member.picker;
}

BReal BViewer::unit(BReal dist) const
{
	if (!member.camera)
		return false;
	int pixelWidth = member.camera->pixelSize().width();
	BReal width = member.camera->lensWidth();
	BReal cnear = member.camera->near();
	BReal hw = pixelWidth * 0.5;
	BReal chw = width * 0.5;
	BReal offsetX = chw / hw;
	return offsetX * dist / cnear;
}
BReal BViewer::unit(const BVector& point) const
{
	if (!member.camera)
		return 1.0;
	BReal dist = (member.camera->position() - point).length();
	return unit(dist);
}

BVector BViewer::vertex(int x, int y, BReal d) const
{
	if (!member.camera)
		return BVector();

	BVector position = member.camera->position();
	BVector center = member.camera->center();
	BVector up = member.camera->up();
	BReal lensWidth = member.camera->lensWidth();
	BReal lensHeight = member.camera->lensHeight();
	BReal cnear = member.camera->near();

	BVector direct = center - position;
	direct.normalize();

	BVector right = direct.cross(up);
	right.normalize();

	up = right.cross(direct);
	up.normalize();

	BReal chw = lensWidth * 0.5;
	BReal chh = lensHeight * 0.5;

	BRect crect = this->centerRect();
	x -= crect.x();
	y -= crect.y();

	BSize pixelSize = member.camera->pixelSize();
	BReal hw = crect.width() * 0.5;
	BReal hh = crect.height() * 0.5;

	BReal offsetX = (x - hw) / hw * chw;
	BReal offsetY = (y - hh) / hh * chh;

	if (member.camera->ortho())
	{
		right *= offsetX;
		up *= offsetY;
	}
	else
	{
		right *= offsetX * d / cnear;
		up *= offsetY * d / cnear;
	}

	BVector base = position + direct * d;
	return base + right - up;
}
BLine BViewer::line(int x, int y) const
{
	if (member.camera)
	{
		BVector begin = vertex(x, y, member.camera->near());
		BVector end = vertex(x, y, member.camera->far());
		return BLine(begin, end);
	}
	return BLine();
}

BVector BViewer::vertex(const BPoint& point, BReal d) const
{
	return vertex(point.x(), point.y(), d);
}
BLine BViewer::line(const BPoint& point) const
{
	return line(point.x(), point.y());
}

BPoint BViewer::point(const BVector vertex)
{
	if (!member.camera)
		return BPoint();
	BPoint point = member.camera->pixelPoint(vertex);
	BRect crect = this->centerRect();
	point += crect.pos();
	return point;
}

BPickItem* BViewer::pick(int x, int y) const
{
	if (!member.camera || !member.scene || member.scene->empty())
		return 0;
	if (member.picker.empty())
		member.picker = new BPicker();
	BLine line = this->line(x, y);
	member.picker->setLine(line);
	return member.picker->apply(member.scene);
}
BPickItem* BViewer::pick(const BPoint& point) const
{
	if (!member.camera || !member.scene || member.scene->empty())
		return 0;
	if (member.picker.empty())
		member.picker = new BPicker();
	BLine line = this->line(point);
	member.picker->setLine(line);
	return member.picker->apply(member.scene);
}

BSize BViewer::sizeHint() const
{
	BSize size = BWidget::sizeHint();
	if (size.width() < 100)
		size.setWidth(100);
	if (size.height() < 100)
		size.setHeight(100);
	return size;
}

bool BViewer::event(const BEvent& event)
{
	if (event.type() == Event_Resize)
	{
		if (member.camera && member.camera->pixelSizePolicy() == Policy_Dynamic)
		{
			const BRect& crect = this->centerRect();
			member.camera->setPixelSize(crect.size());
			BReal aspect = (BReal)crect.width() / crect.height();
			member.camera->setAspect(aspect);
			this->fresh();
		}
	}
	if (event.type() == Event_Paint)
	{
		if (member.camera && member.scene)
		{
			BSet<BCamera*>& cameras = scene_member(member.scene)->cameras;
			for (int i = 0; i < cameras.size(); i++)
			{
				BCamera* camera = cameras[i];
				if (camera != member.camera)
				{
					camera_member(camera)->frame(member.scene);
				}
			}
			camera_member(member.camera)->frame(member.scene, member.handler);
		}
	}
	if (member.handler && member.handler->enabled())
	{
		handler_member(member.handler)->event(event);
	}
	return BWidget::event(event);
}

void BViewer::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	const BRect& crect = this->centerRect();

	painter.bind(member.camera);
	if (IRound* round = widget_member(this)->round)
	{
		glBegin(GL_TRIANGLE_FAN);
		int pointSize = round->centerPoints.size();
		for (int i = 0; i < pointSize; i++)
		{
			const BPointf& p = round->centerPoints[i];
			float tx = (p.x() - crect.x()) / crect.width();
			float ty = (crect.bottom() - p.y()) / crect.height();
			glTexCoord2f(tx, ty);
			glVertex2f(p.x(), p.y());
		}
		glEnd();
	}
	else
	{
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(crect.left(), crect.top());
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(crect.left(), crect.bottom());
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(crect.right(), crect.bottom());
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(crect.right(), crect.top());
		glEnd();
	}
	painter.unbind();

	member.paintHandler(member.handler, painter);
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();
}
